package com.scheduler.backend.blImpl;

import com.scheduler.backend.aps.deadlinefirst.DeadlineFirstScheduler;
import com.scheduler.backend.aps.deadlinefirst.DeadlineFirstTranslator;
import com.scheduler.backend.aps.domain.deadlinefirst.OrderSlice;
import com.scheduler.backend.aps.domain.deadlinefirst.ScheduleTable;
import com.scheduler.backend.bl.ScheduleService;
import com.scheduler.backend.data.repository.*;
import com.scheduler.backend.po.Order;
import com.scheduler.backend.po.OrderSlicePO;
import com.scheduler.backend.po.OrderSliceToEmployeeGroup;
import com.scheduler.backend.po.Resource;
import com.scheduler.backend.vo.ReUseRatio;
import com.scheduler.backend.vo.ResponseVO;
import com.scheduler.backend.vo.TotalLoadRatio;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.optaplanner.core.api.solver.SolverJob;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.time.*;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class ScheduleServiceImpl implements ScheduleService {

    private static Logger logger = (Logger) LoggerFactory.getLogger(ScheduleServiceImpl.class);

    private LocalDateTime scheduleStartTime = LocalDateTime.of(LocalDate.of(2018, 10, 14),
            LocalTime.of(7, 0, 0));//todo 未来修改

    @Autowired
    private ResourceRepository resourceRepository;

    @Autowired
    private ResourceGroupRepository resourceGroupRepository;

    @Autowired
    private OrderSliceRepository orderSliceRepository;

    @Autowired
    private OrderSliceToEmployeeGroupRepository orderSliceToEmployeeGroupRepo;

    @Autowired
    private OrderRepository orderRepository;

    @Autowired
    private DeadlineFirstScheduler deadlineFirstScheduler;

    @Autowired
    private DeadlineFirstTranslator deadlineFirstTranslator;


    @Override
    public ResponseVO startSchedule(LocalDate date) {
        try {
            orderSliceRepository.deleteAll();
            orderSliceToEmployeeGroupRepo.deleteAll();

            // TODO: 修改开始时间
            // LocalDateTime startTime = LocalDateTime.of(date, LocalTime.of(7, 0, 0));
            // ScheduleTable scheduleTable = deadlineFirstTranslator.getSchedulerTable(startTime);
            ScheduleTable scheduleTable = deadlineFirstTranslator.getSchedulerTable(scheduleStartTime);

            UUID problemId = UUID.randomUUID();
            deadlineFirstScheduler.solveAndSave2(problemId, scheduleTable);

            return ResponseVO.buildSuccess();
        } catch (Exception e) {
            return ResponseVO.buildFailure("startSchedule Failed!");
        }
    }

    @Data
    @AllArgsConstructor
    private class Shipment {
        LocalDate date;
        long shipment;
    }

    @Override
    public ResponseVO getShipment(LocalDate date) {
        List<Shipment> shipments = new ArrayList<>();
        for (int i = 0; i < 7; ++i) {
            shipments.add(new Shipment(date.minusDays(6 - i), 0));
        }

        int days = Period.between(scheduleStartTime.toLocalDate(), date).getDays();
        if (days < 0) {
            return ResponseVO.buildSuccess(shipments);
        }
        int daySliceNum = (int) (24 * 60 / ScheduleTable.ORDER_SLICE_MINUTES);
        int endSliceNum = days * daySliceNum;
        int startSliceNum = endSliceNum - 7 * daySliceNum;

        try {
            List<Order> orders = orderRepository.findAll();
            Map<Integer, Order> orderMap = new HashMap<>();
            for (Order order : orders) {
                orderMap.put(order.getOrderId(), order);
            }

            List<OrderSlicePO> orderSliceList = orderSliceRepository.findAll();
            for (OrderSlicePO orderSlice : orderSliceList) {
                int endTimeSlot = (int) orderSlice.getEndTimeSlot();
                if (endTimeSlot < startSliceNum || endTimeSlot >= endSliceNum) {
                    continue;
                }

                OrderSlice slice = new OrderSlice();
                slice.setId(orderSlice.getId());
                Order order = orderMap.get(slice.getOrderId());
                int cnt = order.getOrderNum() / slice.getItemNum() + 1;
                int idx = (endTimeSlot - startSliceNum) / daySliceNum;

                Shipment shipment = shipments.get(idx);
                shipment.setShipment(shipment.getShipment() + cnt);
            }

            return ResponseVO.buildSuccess(shipments);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseVO.buildFailure("getShipment Failed!");
        }
    }

    @Override
    public ResponseVO getPunctualRatioByTime(Date date) {
        try {
            double punctualRatio = 0.0f;
            int allOrderNum = 0;
            int okOrderNum = 0;

            List<Order> orderList = orderRepository.findAll();
            List<OrderSlicePO> orderSliceList = orderSliceRepository.findAll();
            orderSliceList.sort(Comparator.comparingLong(OrderSlicePO::getStartTimeSlot));

            allOrderNum = orderList.size();
            okOrderNum = orderList.size();
            for (int i = orderList.size() - 1; i >= 0; i--) {
                Order order = orderList.get(i);
                LocalDateTime ddl = order.getDeadline().toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime();
                LocalDateTime today = date.toInstant()
                        .atZone(ZoneId.systemDefault())
                        .toLocalDateTime();
                for (int j = 0; j < orderSliceList.size(); j++) {
                    if (!orderSliceList.get(j).getOrderID().equals(order.getOrderId().toString())) continue;
                    LocalDateTime endTime = scheduleStartTime.plusSeconds((orderSliceList.get(j).getStartTimeSlot() + ScheduleTable.ORDER_SLICE_TIMESLOT_SPAN) * 60 * ScheduleTable.ORDER_SLICE_MINUTES);
                    if (endTime.isAfter(ddl) && !ddl.isAfter(today)) {
                        okOrderNum--;
                    }
                    break;
                }
            }

            if (allOrderNum != 0) {
                punctualRatio = ((double) okOrderNum) / ((double) allOrderNum);
            }
            punctualRatio = (double) Math.round(punctualRatio * 1000) / 10;
            return ResponseVO.buildSuccess(punctualRatio);
        } catch (Exception e) {
            return ResponseVO.buildFailure("getPunctualRatioByTime Failed!");
        }
    }

    @Override
    public ResponseVO getResourceUseRatioByTimeScope(Date startTime, Date endTime) {
        return null;
    }

    @Override
    public ResponseVO getResourceLoadRatioByDateScope(Date startTime0, Date endTime0) {
        try {
            List<ReUseRatio> reUseRatioList = new ArrayList<>();

            List<Resource> newResources = resourceRepository.findAll();
            List<OrderSlicePO> orderSliceList = orderSliceRepository.findAll();
            orderSliceList.sort(Comparator.comparingLong(OrderSlicePO::getStartTimeSlot));


            LocalDateTime today = startTime0.toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDateTime();
            LocalDateTime tomorrow = endTime0.toInstant()
                    .atZone(ZoneId.systemDefault())
                    .toLocalDateTime();

            double equipSumUp = 0.0;
            double equipSumDown = 0.0;
            double staffSumUp = 0.0;
            double staffSumDown = 0.0;

            List<OrderSliceToEmployeeGroup> oldList = resourceGroupRepository.findAll();

            for (int i = 0; i < newResources.size(); i++) {
                Resource resource = newResources.get(i);
                double ratio = 0.0;
                double sumUp = 0.0;
                double sumDown = Duration.between(today, tomorrow).toMinutes();
                for (int j = 0; j < orderSliceList.size(); j++) {
                    if (resource.getResourceName().equals("线体")) {
                        if (!orderSliceList.get(j).getEquipmentID().startsWith(resource.getResourceId())) continue;
                    } else {
                        boolean pd = false;
                        List<OrderSliceToEmployeeGroup> newList = new ArrayList<>();
                        for (int k = 0; k < oldList.size(); k++) {
                            if (orderSliceList.get(j).getId().startsWith(oldList.get(k).getId())) {
                                newList.add(oldList.get(k));
                            }
                        }
                        for (int k = 0; k < newList.size(); k++) {
                            if (newList.get(k).getEmployeeGroupID().equals(resource.getResourceId())) {
                                pd = true;
                            }
                        }
                        if (!pd) continue;
                    }

                    LocalDateTime startTime = scheduleStartTime.plusSeconds(orderSliceList.get(j).getStartTimeSlot() * 60 * ScheduleTable.ORDER_SLICE_MINUTES);
                    LocalDateTime endTime = scheduleStartTime.plusSeconds((orderSliceList.get(j).getStartTimeSlot() + ScheduleTable.ORDER_SLICE_TIMESLOT_SPAN) * 60 * ScheduleTable.ORDER_SLICE_MINUTES);

                    if (!startTime.isBefore(today) && !endTime.isAfter(tomorrow)) {
                        sumUp += Math.abs(Duration.between(endTime, startTime).toMinutes());
                    } else if (!startTime.isBefore(today) && !startTime.isAfter(tomorrow)) {
                        sumUp += Math.abs(Duration.between(tomorrow, startTime).toMinutes());
                    } else if (!endTime.isAfter(tomorrow) && !endTime.isBefore(today)) {
                        sumUp += Math.abs(Duration.between(today, endTime).toMinutes());
                    } else if (!startTime.isAfter(today) && !endTime.isBefore(tomorrow)) {
                        sumUp += Math.abs(Duration.between(today, tomorrow).toMinutes());
                    }
                }
                if (resource.getResourceName().equals("线体")) {
                    equipSumUp += sumUp;
                    equipSumDown += sumDown;
                } else {
                    staffSumUp += sumUp;
                    staffSumDown += sumDown;
                }

            }

            TotalLoadRatio totalLoadRatio = new TotalLoadRatio();

            double staffRt = 0.0;
            if (staffSumDown != 0) {
                staffRt = (double) staffSumUp / (double) staffSumDown;
            }

            double equipRt = 0.0;
            if (equipSumDown != 0) {
                equipRt = (double) equipSumUp / (double) equipSumDown;
            }

            staffRt = (double) Math.round(staffRt * 1000) / 10;
            equipRt = (double) Math.round(equipRt * 1000) / 10;

            totalLoadRatio.setDeviceLoadRatio(equipRt);
            totalLoadRatio.setStaffLoadRatio(staffRt);

            return ResponseVO.buildSuccess(totalLoadRatio);
        } catch (Exception e) {
            return ResponseVO.buildFailure("getResourceLoadRatioByDateScope Failed!");
        }
    }
}